﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Pixysoft.Framework.Reflection.Controller;
using Pixysoft.Framework.Verifications;

namespace Pixysoft.Framework.Reflection.Core
{
    class DynamicType : IDynamicType
    {

        Type type = null;

        public DynamicType(Type type)
        {
            this.type = type;
        }


        public IDynamicConstructorInfo GetDefaultConstructor()
        {
            return GetConstructor(Type.EmptyTypes);
        }

        public IDynamicConstructorInfo GetConstructor(Type[] types)
        {
            if (types == null)
                throw Exceptions.VerificationFailedException(types);

            StringBuilder builder = new StringBuilder();

            builder.Append(type.Namespace).Append(type.FullName).Append(type.Name);

            if (types != null)
            {
                foreach (Type ptype in types)
                {
                    builder.Append(ptype.Namespace).Append(ptype.FullName).Append(ptype.Name);
                }
            }

            string key = builder.ToString();

            DynamicConstructorInfoHandler handler = null;

            DynamicCacheFactory<string, DynamicConstructorInfoHandler>.Instance.WriteLock();

            try
            {
                if (DynamicCacheFactory<string, DynamicConstructorInfoHandler>.Instance.Contains(key))
                {
                    handler = DynamicCacheFactory<string, DynamicConstructorInfoHandler>.Instance.GetValue(key);
                }
                else
                {
                    ConstructorInfo info = type.GetConstructor(types);

                    if (info == null)
                        return null;

                    handler = DynamicMethodFactory.CreateDynamicConstructorInfoHandler(type, info);

                    DynamicCacheFactory<string, DynamicConstructorInfoHandler>.Instance.AddValue(key, handler);
                }
            }
            finally
            {
                DynamicCacheFactory<string, DynamicConstructorInfoHandler>.Instance.WriteUnlock();
            }

            return new DynamicConstructorInfo(handler);
        }



        public IDynamicFieldInfo GetField(string name)
        {
            //不使用verification 从 1,599ms -> 1,424ms

            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name);

            return GetField(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
        }

        public IDynamicFieldInfo GetField(string name, BindingFlags bindingAttr)
        {
            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name, bindingAttr);

            //循环 100000 使用stringbuilder = 2,033ms， 使用string = 1,621ms

            //枚举转变为string 消耗接近了1s

            //由于field不可能重名，因此根本不需要添加枚举

            string key = type.Namespace + type.FullName + type.Name;

            string setkey = key + "set";

            string getkey = key + "get";

            DynamicFieldSetHandler setHandler = null;

            DynamicFieldGetHandler getHandler = null;

            DynamicCacheFactory<string, DynamicFieldSetHandler>.Instance.WriteLock();

            try
            {
                if (DynamicCacheFactory<string, DynamicFieldSetHandler>.Instance.Contains(setkey))
                {
                    setHandler = DynamicCacheFactory<string, DynamicFieldSetHandler>.Instance.GetValue(setkey);
                }

                if (DynamicCacheFactory<string, DynamicFieldGetHandler>.Instance.Contains(getkey))
                {
                    getHandler = DynamicCacheFactory<string, DynamicFieldGetHandler>.Instance.GetValue(getkey);
                }

                if (setHandler == null || getHandler == null)
                {
                    FieldInfo info = type.GetField(name, bindingAttr);

                    if (info == null)
                        return null;

                    setHandler = DynamicMethodFactory.CreateSetHandler(type, info);

                    getHandler = DynamicMethodFactory.CreateGetHandler(type, info);

                    DynamicCacheFactory<string, DynamicFieldSetHandler>.Instance.AddValue(setkey, setHandler);

                    DynamicCacheFactory<string, DynamicFieldGetHandler>.Instance.AddValue(getkey, getHandler);
                }
            }
            finally
            {
                DynamicCacheFactory<string, DynamicFieldSetHandler>.Instance.WriteUnlock();
            }

            return new DynamicFieldInfo(setHandler, getHandler);
        }




        public IDynamicPropertyInfo GetProperty(string name)
        {
            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name);

            return GetProperty(name, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
        }

        public IDynamicPropertyInfo GetProperty(string name, BindingFlags bindingAttr)
        {
            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name, bindingAttr);

            string key = type.Namespace + type.FullName + type.Name + name;

            string setkey = key + "set";

            string getkey = key + "get";


            DynamicPropertySetHandler setHandler = null;

            DynamicPropertyGetHandler getHandler = null;

            DynamicCacheFactory<string, DynamicPropertySetHandler>.Instance.WriteLock();

            try
            {
                if (DynamicCacheFactory<string, DynamicPropertySetHandler>.Instance.Contains(setkey))
                {
                    setHandler = DynamicCacheFactory<string, DynamicPropertySetHandler>.Instance.GetValue(setkey);
                }

                if (DynamicCacheFactory<string, DynamicPropertyGetHandler>.Instance.Contains(getkey))
                {
                    getHandler = DynamicCacheFactory<string, DynamicPropertyGetHandler>.Instance.GetValue(getkey);
                }

                if (setHandler == null || getHandler == null)
                {
                    PropertyInfo info = type.GetProperty(name, bindingAttr);

                    if (info == null)
                        return null;

                    setHandler = DynamicMethodFactory.CreateSetHandler(type, info);

                    getHandler = DynamicMethodFactory.CreateGetHandler(type, info);

                    DynamicCacheFactory<string, DynamicPropertySetHandler>.Instance.AddValue(setkey, setHandler);

                    DynamicCacheFactory<string, DynamicPropertyGetHandler>.Instance.AddValue(getkey, getHandler);
                }
            }
            finally
            {
                DynamicCacheFactory<string, DynamicPropertySetHandler>.Instance.WriteUnlock();
            }

            return new DynamicPropertyInfo(setHandler, getHandler);
        }

        public IDynamicPropertyInfo[] GetProperties()
        {
            List<IDynamicPropertyInfo> list = new List<IDynamicPropertyInfo>();

            foreach (PropertyInfo info in type.GetProperties())
            {
                list.Add(GetProperty(info.Name));
            }

            return list.ToArray();
        }



        public IDynamicMethodInfo GetMethod(string name)
        {
            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name);

            return GetMethod(name, Type.EmptyTypes);
        }

        public IDynamicMethodInfo GetMethod(string name, Type[] types)
        {
            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name);

            StringBuilder builder = new StringBuilder();

            builder.Append(type.Namespace).Append(type.FullName).Append(type.Name);

            if (types != null)
            {
                foreach (Type ptype in types)
                {
                    builder.Append(ptype.Namespace).Append(ptype.FullName).Append(ptype.Name);
                }
            }

            builder.Append(name);

            string key = builder.ToString();

            DynamicMethodHandler handler = null;

            DynamicCacheFactory<string, DynamicMethodHandler>.Instance.WriteLock();

            try
            {
                if (DynamicCacheFactory<string, DynamicMethodHandler>.Instance.Contains(key))
                {
                    handler = DynamicCacheFactory<string, DynamicMethodHandler>.Instance.GetValue(key);
                }
                else
                {
                    MethodInfo info = type.GetMethod(name, types);

                    if (info == null)
                        return null;

                    handler = DynamicMethodFactory.CreateDynamicMethodInfoHandler(type, info);

                    DynamicCacheFactory<string, DynamicMethodHandler>.Instance.AddValue(key, handler);
                }
            }
            finally
            {
                DynamicCacheFactory<string, DynamicMethodHandler>.Instance.WriteUnlock();
            }

            return new DynamicMethodInfo(handler);
        }

        public IDynamicMethodInfo GetMethod(IDummyMethodInfo methodInfo)
        {
            if (!Verification.Verifies(methodInfo).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(methodInfo);

            StringBuilder builder = new StringBuilder();

            builder.Append(type.Namespace).Append(type.FullName).Append(type.Name);

            foreach (IDummyParameterInfo parameter in methodInfo.GetParameters())
            {
                IDummyType ptype = parameter.ParameterType;

                builder.Append(ptype.Namespace).Append(ptype.FullName).Append(ptype.Name);
            }

            builder.Append(methodInfo.Name);

            string key = builder.ToString();

            DynamicMethodHandler handler = null;

            DynamicCacheFactory<string, DynamicMethodHandler>.Instance.WriteLock();

            try
            {

                if (DynamicCacheFactory<string, DynamicMethodHandler>.Instance.Contains(key))
                {
                    handler = DynamicCacheFactory<string, DynamicMethodHandler>.Instance.GetValue(key);
                }
                else
                {
                    MethodInfo info = null;

                    foreach (MethodInfo searchinfo in type.GetMethods())
                    {
                        if (!string.Equals(searchinfo.Name, methodInfo.Name))
                            continue;

                        ParameterInfo[] paras = searchinfo.GetParameters();

                        IDummyParameterInfo[] dummyparas = methodInfo.GetParameters();

                        if (paras.Length != dummyparas.Length)
                            continue;

                        if (paras.Length == 0)
                        {
                            info = searchinfo;

                            break;
                        }

                        for (int i = 0; i < paras.Length; i++)
                        {
                            Type paraType = paras[i].ParameterType;

                            IDummyType dummyParaType = dummyparas[i].ParameterType;

                            if (!string.Equals(paraType.Namespace, dummyParaType.Namespace))
                                continue;

                            if (!string.Equals(paraType.FullName, dummyParaType.FullName))
                                continue;

                            if (!string.Equals(paraType.Name, dummyParaType.Name))
                                continue;

                            info = searchinfo;

                            break;
                        }

                        if (info != null)
                            break;
                    }

                    if (info == null)
                        return null;

                    handler = DynamicMethodFactory.CreateDynamicMethodInfoHandler(type, info);

                    DynamicCacheFactory<string, DynamicMethodHandler>.Instance.AddValue(key, handler);
                }
            }
            finally
            {
                DynamicCacheFactory<string, DynamicMethodHandler>.Instance.WriteUnlock();
            }

            return new DynamicMethodInfo(handler);
        }

        public IDynamicMethodInfo GetMethod(MethodInfo methodInfo)
        {
            if (methodInfo == null)
                throw Exceptions.VerificationFailedException(methodInfo);

            StringBuilder builder = new StringBuilder();

            builder.Append(type.Namespace).Append(type.FullName).Append(type.Name);

            foreach (ParameterInfo parameter in methodInfo.GetParameters())
            {
                Type ptype = parameter.ParameterType;

                builder.Append(ptype.Namespace).Append(ptype.FullName).Append(ptype.Name);
            }

            builder.Append(methodInfo.Name);

            string key = builder.ToString();

            DynamicMethodHandler handler = null;

            DynamicCacheFactory<string, DynamicMethodHandler>.Instance.WriteLock();

            try
            {
                if (DynamicCacheFactory<string, DynamicMethodHandler>.Instance.Contains(key))
                {
                    handler = DynamicCacheFactory<string, DynamicMethodHandler>.Instance.GetValue(key);
                }
                else
                {
                    handler = DynamicMethodFactory.CreateDynamicMethodInfoHandler(type, methodInfo);

                    DynamicCacheFactory<string, DynamicMethodHandler>.Instance.AddValue(key, handler);
                }
            }
            finally
            {
                DynamicCacheFactory<string, DynamicMethodHandler>.Instance.WriteUnlock();
            }

            return new DynamicMethodInfo(handler);
        }

    }
}
